#ifndef CODE_LINE_CPP
#define CODE_LINE_CPP

#include <vector>
#include <algorithm>

#include "stdafx.h"
#include "UmlExt.h"
#include "GOAL.h"
#include "CodeLine.h"
#include "PositionInfo.h"

/// constructor
GOAL::Code_Line_Iterator::Code_Line_Iterator(Code_Line & code_line,
                                             bool end)
         : code_line_ (code_line)
{
  pos_ = (end ? code_line.objects_.size () : 0 );
}

/// copy constructor
GOAL::Code_Line_Iterator::Code_Line_Iterator(const Code_Line_Iterator& iterator)
: code_line_ (iterator.code_line_), pos_ (iterator.pos_)
{
}

/// destructor
GOAL::Code_Line_Iterator::~Code_Line_Iterator()
{
}

/// Dereference operator returns a reference to the item contained
/// at the current position
GOAL::MgaObject& 
GOAL::Code_Line_Iterator::operator* (void)
{
  // note that I'm not checking the pos_. It's up to the user
  // to check for end.
  return code_line_.objects_[pos_];
}

/// Returns a const reference to the item contained at the current position
const GOAL::MgaObject& 
GOAL::Code_Line_Iterator::operator* (void) const
{
  // note that I'm not checking the pos_. It's up to the user
  // to check for end.
  return code_line_.objects_[pos_];
}

/// Preincrement operator
GOAL::Code_Line_Iterator &
GOAL::Code_Line_Iterator::operator++ (void)
{
  ++pos_;
  return *this;
}

    /// Postincrement operator
GOAL::Code_Line_Iterator 
GOAL::Code_Line_Iterator::operator++ (int)
{
  GOAL::Code_Line_Iterator newer (this->code_line_);

  ++pos_;

  return newer;
}

/// Minus operator (needed for std::sort because of its optimizations).
int GOAL::Code_Line_Iterator::operator- (const Code_Line_Iterator& rhs)
{
  return this->pos_ - rhs.pos_;
}

/// Minus operator (needed for std::sort because of its optimizations).
//int GOAL::Code_Line_Iterator::operator+ (const Code_Line_Iterator& rhs);
//{
//  return this->pos_ + rhs.pos_;
//}

/// Plus operator (needed for std::sort because of its optimizations).
GOAL::Code_Line_Iterator 
GOAL::Code_Line_Iterator::operator+ (int increment)
{
  GOAL::Code_Line_Iterator newer (this->code_line_);

  newer.pos_ += increment;

  return newer;
}

/// Plus operator (needed for std::sort because of its optimizations).
GOAL::Code_Line_Iterator 
GOAL::Code_Line_Iterator::operator+ (int increment) const
{
  GOAL::Code_Line_Iterator newer (*this);

  newer.pos_ += increment;

  return newer;
}

/// Minus operator (needed for std::sort because of its optimizations).
int 
GOAL::Code_Line_Iterator::operator- (int increment)
{
  return this->pos_ - increment;
}


GOAL::Code_Line_Iterator & 
GOAL::Code_Line_Iterator::operator= (const Code_Line_Iterator& rhs)
{
  this->pos_ = rhs.pos_;
  this->code_line_ = rhs.code_line_;

  return *this;
}

/// Equality operator
bool 
GOAL::Code_Line_Iterator::operator== (const Code_Line_Iterator &rhs) const
{
  return (&(this->code_line_) ==  &(rhs.code_line_) && 
           this->pos_ == rhs.pos_);
}

/// Nonequality operator
bool 
GOAL::Code_Line_Iterator::operator!= (const Code_Line_Iterator &rhs) const
{
  return ! (*this == rhs);
}

GOAL::Code_Line::Code_Line()
: position (0-1,0-1)
{

}

GOAL::Code_Line::Code_Line(const std::vector <GOAL::MgaObject> & source)
: position (0-1,0-1), objects_(source)
{
  
}

GOAL::Code_Line::Code_Line(const Code_Line & source)
: position (source.position), objects_(source.objects_), 
  object_lookups_(source.object_lookups_)
{
  
}

GOAL::Code_Line::~Code_Line()
{

}

bool 
GOAL::Code_Line::lessThan(const MgaObject& left, const MgaObject& right)
{
  return 
    GME::Position::strip_position (left.position ().operator std::string ()).first < 
    GME::Position::strip_position (right.position ().operator std::string ()).first;
}

// Returns a reference to the <index> element in the Code_Line without
// checking for range errors.
const GOAL::MgaObject &
GOAL::Code_Line::operator[] (size_t index) const
{
  return objects_[index];
}

bool 
GOAL::Code_Line::operator<(const Code_Line& rhs) const
{
  // most likely comparison
  if (this->position.second < rhs.position.second)
    return true;

  // take care of the other most likely candidate
  else if (this->position.second > rhs.position.second)
    return false;

  // outlier - if we have two codelines with the same y value,
  // then we have to check the x value. This follows English grammar
  // rules where something at the top of the page comes before something
  // at the bottom of the page, no matter where they are positioned left-
  // to-right.
  else
    return this->position.first < this->position.second;
}


GOAL::Code_Line GOAL::Code_Line::getSorted ()
{
  Code_Line newCopy (*this);
  std::sort (newCopy.objects_.begin (), newCopy.objects_.end (), GOAL::Code_Line::lessThan);

  return newCopy;
}

void
GOAL::Code_Line::insert (const MgaObject& object)
{
  // after some deliberation, I decided to make Code_Line
  // not very intelligent, and allow the omnipotent controller
  // of it simply direct it to insert something. 

  // the only intelligence here is to check if the object already exists
  // we could have gotten around this with std::set. However, I wanted the
  // lookup for semantic level checking during compilation - just in case I
  // need to check for objects shared between two code lines

  if (objects_.begin () != objects_.end () && 
      parent () != object.parent ())
    return;

  if (exists (object))
    return;

  // do we need to allocate more objects?
  if (objects_.size () % 10 == 9)
//    objects_.resize (objects_.size () + 10);
    objects_.reserve (objects_.size () + 10);

  // update the lookup
  object_lookups_[object] = objects_.size ();
  
  // add the object
  objects_.push_back (object);

  std::string position = object.position ().operator std::string ();
  
  GME::Position::Pair coords = 
    GME::Position::strip_position (position);

  if (coords.first < this->position.first)
    this->position.first = coords.first;

  if (coords.second < this->position.second)
    this->position.second = coords.second;
}

Udm::Object GOAL::Code_Line::parent ()
{
  return objects_.begin ()->parent ();
}

/// iterator functions
unsigned int
GOAL::Code_Line::size ()
{
  return this->objects_.size ();
}

bool 
GOAL::Code_Line::exists (const GOAL::MgaObject& object)
{  
  return this->object_lookups_.find (object) != this->object_lookups_.end ();
}

std::string GOAL::Code_Line::output (void)
{
  std::string outputString;

  for (unsigned int i = 0; i < objects_.size (); ++i)
    {
      outputString += objects_[i].type ().name ();

    }

  return outputString;
}

/// iterator functions
GOAL::Code_Line::iterator
GOAL::Code_Line::begin ()
{
  return GOAL::Code_Line::iterator (*this);
}

GOAL::Code_Line::iterator 
GOAL::Code_Line::end ()
{
  return GOAL::Code_Line::iterator (*this, true);
}

#endif
